package com.adobe.apprise.commands
{
	import com.adobe.apprise.aggregator.Aggregator;
	import com.adobe.apprise.aggregator.AggregatorEvent;
	import com.adobe.apprise.aggregator.AggregatorResponder;
	import com.adobe.apprise.database.DatabaseEvent;
	import com.adobe.apprise.database.DatabaseResponder;
	import com.adobe.apprise.events.AggregateEvent;
	import com.adobe.apprise.events.PopulateViewControlEvent;
	import com.adobe.apprise.model.ModelLocator;
	import com.adobe.cairngorm.commands.ICommand;
	import com.adobe.cairngorm.control.CairngormEvent;
	import com.adobe.xml.syndication.generic.Author;
	import com.adobe.xml.syndication.generic.IFeed;
	import com.adobe.xml.syndication.generic.IItem;
	
	import flash.events.IOErrorEvent;
	
	import mx.collections.ArrayCollection;
	import mx.formatters.DateFormatter;
	import mx.resources.ResourceManager;

	public class AggregateCommand implements ICommand
	{
		private var dateFormatter:DateFormatter;

		public function AggregateCommand()
		{
			this.dateFormatter = new DateFormatter();
			dateFormatter.formatString = ResourceManager.getInstance().getString('resources', 'AGGREGATECOMMAND_DATE_FORMATTER');
		}

		public function execute(ce:CairngormEvent):void
		{
			if (ModelLocator.getInstance().currentlyAggregating) return;
			ModelLocator.getInstance().currentlyAggregating = true;
			var state:Object = new Object();
			state.feeds = AggregateEvent(ce).feeds;
			this.aggregate(state);
		}
		
		private function aggregate(state:Object):void
		{
			var feeds:Array = state.feeds as Array;
			
			if (ModelLocator.getInstance().db.aConn.inTransaction)
			{
				ModelLocator.getInstance().db.aConn.commit();
			}
			
			if (feeds == null || feeds.length == 0 || ModelLocator.getInstance().stopAggregating)
			{
				new PopulateViewControlEvent().dispatch();
				ModelLocator.getInstance().statusMessage = ResourceManager.getInstance().getString('resources', 'AGGREGATECOMMAND_LAST_UPDATED_STATUS').replace("$1", this.dateFormatter.format(new Date()));
				ModelLocator.getInstance().currentlyAggregating = false;
				ModelLocator.getInstance().stopAggregating = false;
				return;
			}
			
			var feedUrl:String = feeds.shift();
			var aggregator:Aggregator = new Aggregator();
			var aggregatorResponder:AggregatorResponder = new AggregatorResponder();

			ModelLocator.getInstance().currentAggregatorResponder = aggregatorResponder;

			aggregatorResponder.addEventListener(AggregatorEvent.ERROR_EVENT,
				function(aggErrorEvent:AggregatorEvent):void
				{
					trace("AGGREGATOR ERROR", aggErrorEvent.data, feedUrl);
					state.feedUrl = feedUrl;
					state.errorMessage = "Unable to aggregate: " + aggErrorEvent.data;
					handleFeedError(state);
				});

			aggregatorResponder.addEventListener(IOErrorEvent.IO_ERROR,
				function(ioErrorEvent:IOErrorEvent):void
				{
					trace("IOError", ioErrorEvent, feedUrl);
					state.feedUrl = feedUrl;
					state.errorMessage = "Network error: " + ioErrorEvent.text;
					handleFeedError(state);
				});

			aggregatorResponder.addEventListener(AggregatorEvent.FEED_EVENT,
				function(feedEvent:AggregatorEvent):void
				{
					var feed:IFeed = feedEvent.data as IFeed;
					var responder:DatabaseResponder = new DatabaseResponder();
					responder.addEventListener(DatabaseEvent.RESULT_EVENT,
						function(e:DatabaseEvent):void
						{
							var feedId:Number = e.data as Number;
							if (!ModelLocator.getInstance().db.aConn.inTransaction)
							{
								ModelLocator.getInstance().db.aConn.begin();
							}
							state.newPostCount = 0;
							if (feedId == -1) // New feed.
							{
								try
								{
									var responder2:DatabaseResponder = new DatabaseResponder();
									responder2.addEventListener(DatabaseEvent.RESULT_EVENT,
										function(ee:DatabaseEvent):void
										{
											state.feedId = ee.data as Number;
											state.posts = feed.items;
											insertPost(state);
										});
									ModelLocator.getInstance().db.insertFeed(responder2, feedUrl, feed);
								}
								catch (e:Error)
								{
									// Nothing we can do at this point.  Silently skip it.
									trace("ERROR INSERTING FEED.", e.message, feedUrl);
								}
							}
							else // Existing feed.
							{
								try
								{
									var responder3:DatabaseResponder = new DatabaseResponder();
									responder3.addEventListener(DatabaseEvent.RESULT_EVENT,
										function(ee:DatabaseEvent):void
										{
											state.feedId = feedId;
											state.posts = feed.items;
											insertPost(state);
										});
									ModelLocator.getInstance().db.updateFeed(responder3, feedId, feedUrl, feed);
								}
								catch (e:Error)
								{
									// Nothing we can do at this point.  Silently skip it.
									trace("ERROR UPDATING FEED.", e.message, feedUrl);
								}
							}
						});
					ModelLocator.getInstance().db.getFeedIdByFeedUrl(responder, feedUrl);
				});

			ModelLocator.getInstance().statusMessage = ResourceManager.getInstance().getString('resources', 'AGGREGATECOMMAND_AGGREGATING_STATUS').replace("$1", feedUrl);
			aggregator.getFeed(aggregatorResponder, feedUrl);
		}

		private function refreshCounts(state:Object):void 
		{
			var ml:ModelLocator = ModelLocator.getInstance();
			var feeds:ArrayCollection = ml.feeds;
			var totals:ArrayCollection = ml.totals;

			//update total unread post counts for this feed
			for each ( var o:Object in feeds )
			{
				if ( o.id == state.feedId )
				{
					o.unread += state.newPostCount;
					totals[0].count += state.newPostCount;
					
					feeds.itemUpdated(o,"unread");
					totals.itemUpdated(o,"unread"); 
					break;
				}
			}
			this.aggregate(state);		
		}
		
		private function insertPost(state:Object):void
		{
			var posts:Array = state.posts;
			if (posts == null || posts.length == 0)
			{
				this.refreshCounts(state);
				return;
			}
			var post:IItem = state.posts.shift() as IItem;
			var responder:DatabaseResponder = new DatabaseResponder();
			responder.addEventListener(DatabaseEvent.RESULT_EVENT,
				function(e:DatabaseEvent):void
				{
					var existingPost:Object = e.data;
					if (existingPost == null)
					{
						var responder2:DatabaseResponder = new DatabaseResponder();
						responder2.addEventListener(DatabaseEvent.RESULT_EVENT,
							function(e:DatabaseEvent):void
							{
								state.post = post;
								state.postId = e.data as Number;
								state.authors = post.authors;
								insertAuthors(state);
								state.newPostCount += 1;
							});
						ModelLocator.getInstance().db.insertPost(responder2, state.feedId, post);
					}
					else
					{
						var responder3:DatabaseResponder = new DatabaseResponder();
						responder3.addEventListener(DatabaseEvent.RESULT_EVENT,
							function(e:DatabaseEvent):void
							{
								state.post = post;
								state.postId = existingPost.id;
								state.authors = post.authors;
								deleteAuthors(state);
							});
						ModelLocator.getInstance().db.updatePost(responder3, existingPost, post);
					}
				});
			//Use <guid>, if available
			if ( post.id != null ) 
			{
				ModelLocator.getInstance().db.getPostByGuid(responder, post.id);
			}
			else 
			{
				ModelLocator.getInstance().db.getPostByTitle(responder, post.title);
			}
		}
		
		private function deleteAuthors(state:Object):void
		{
			var responder:DatabaseResponder = new DatabaseResponder();
			responder.addEventListener(DatabaseEvent.RESULT_EVENT,
				function(e:DatabaseEvent):void
				{
					deleteTopics(state);
				});
			ModelLocator.getInstance().db.deleteAuthorsByPostId(responder, state.postId as Number);
		}

		private function deleteTopics(state:Object):void
		{
			var responder:DatabaseResponder = new DatabaseResponder();
			responder.addEventListener(DatabaseEvent.RESULT_EVENT,
				function(e:DatabaseEvent):void
				{
					state.authors = state.post.authors;
					insertAuthors(state);
				});
			ModelLocator.getInstance().db.deleteTopicsByPostId(responder, state.postId as Number);
		}
		
		private function insertAuthors(state:Object):void
		{
			if (state.authors == null || state.authors.length == 0)
			{
				state.topics = state.post.topics;
				insertTopics(state);
				return;
			}

			var author:Author = state.authors.shift() as Author;
			if (author.name == null)
			{
				insertAuthors(state);
				return;
			}

			var responder:DatabaseResponder = new DatabaseResponder();
			responder.addEventListener(DatabaseEvent.RESULT_EVENT,
				function(e:DatabaseEvent):void
				{
					insertAuthors(state);
				});
			ModelLocator.getInstance().db.insertAuthor(responder, state.postId, author.name);
		}
		
		private function insertTopics(state:Object):void
		{
			if (state.topics == null || state.topics.length == 0)
			{
				insertPost(state);
				return;
			}

			var topic:String = state.topics.shift();
			if (topic == null)
			{
				insertTopics(state);
				return;
			}

			var responder:DatabaseResponder = new DatabaseResponder();
			responder.addEventListener(DatabaseEvent.RESULT_EVENT,
				function(e:DatabaseEvent):void
				{
					insertTopics(state);
				});
			ModelLocator.getInstance().db.insertTopic(responder, state.postId, topic);
		}
		
		private function handleFeedError(state:Object):void
		{
			var responder:DatabaseResponder = new DatabaseResponder();
			responder.addEventListener(DatabaseEvent.RESULT_EVENT,
				function(e:DatabaseEvent):void
				{
					var feedId:Number = e.data as Number;
					if (feedId == -1) // New broken feed.
					{
						insertBadFeed(state);
					}
					else  // Existing broken feed.
					{
						updateBadFeed(state);
					}
				});
			ModelLocator.getInstance().db.getFeedIdByFeedUrl(responder, state.feedUrl);
		}
		
		private function insertBadFeed(state:Object):void
		{
			var responder:DatabaseResponder = new DatabaseResponder();
			responder.addEventListener(DatabaseEvent.RESULT_EVENT,
				function(e:DatabaseEvent):void
				{
					aggregate(state);
				});
			ModelLocator.getInstance().db.insertBadFeed(responder, state.feedUrl, state.errorMessage);
		}

		private function updateBadFeed(state:Object):void
		{
			var responder:DatabaseResponder = new DatabaseResponder();
			responder.addEventListener(DatabaseEvent.RESULT_EVENT,
				function(e:DatabaseEvent):void
				{
					aggregate(state);
				});
			ModelLocator.getInstance().db.updateBadFeed(responder, state.feedUrl, state.errorMessage);
		}
	}
}
